Książki o języku programowania wyjaśniają, że typy wartości są tworzone na stosie, a typy referencyjne na stercie, bez wyjaśniania, czym są te dwie rzeczy. Nie przeczytałem jasnego wyjaśnienia tego. Rozumiem, co to jest stos. Ale, Gdzie i czym one są (fizycznie w pamięci prawdziwego komputera)? W jakim stopniu są one kontrolowane przez system operacyjny lub czas wykonywania języka? Jaki jest ich zakres? Od czego zależy wielkość każdego z nich? Co sprawia, że jest szybszy?
2020-12-07 21:43:19
Stos jest pamięcią zarezerwowaną jako miejsce na zarysowania dla wątku wykonania. Gdy wywoływana jest funkcja, na szczycie stosu rezerwowany jest blok dla zmiennych lokalnych i niektórych danych księgowych. Gdy ta funkcja powróci, blok staje się nieużywany i może być użyty przy następnym wywołaniu funkcji. Stos jest zawsze rezerwowany w kolejności LIFO (ostatnie w kolejności pierwsze wyszło); ostatnio zarezerwowany blok jest zawsze następnym zwolnionym blokiem. To sprawia, że śledzenie stosu jest naprawdę proste; uwolnienie bloku ze stosu to nic innego jak dostosowanie jednego wskaźnika. Sterta to pamięć zarezerwowana na potrzeby alokacji dynamicznej. W przeciwieństwie do stosu, nie ma wymuszonego wzorca przydziału i zwalniania bloków ze sterty; możesz przydzielić blok w dowolnym momencie i zwolnić go w dowolnym momencie. To sprawia, że śledzenie, które części sterty są przydzielone lub wolne w danym momencie, jest znacznie bardziej skomplikowane; Dostępnych jest wiele niestandardowych alokatorów sterty do dostrajania wydajności sterty dla różnych wzorców użycia. Każdy wątek otrzymuje stos, podczas gdy zwykle jest tylko jedna sterta dla aplikacji (chociaż nie jest rzadkością posiadanie wielu stert dla różnych typów alokacji). Aby odpowiedzieć bezpośrednio na pytania: W jakim stopniu są one kontrolowane przez system operacyjny lub środowisko wykonawcze języka? System operacyjny przydziela stos dla każdego wątku na poziomie systemu podczas tworzenia wątku. Zazwyczaj system operacyjny jest wywoływany przez środowisko wykonawcze języka w celu przydzielenia stosu aplikacji. Jaki jest ich zakres? Stos jest przymocowany do nici, więc gdy nić wyjdzie, stos jest odzyskiwany. Sterta jest zwykle przydzielana podczas uruchamiania aplikacji przez środowisko wykonawcze i odzyskiwana, gdy aplikacja (technicznie proces) kończy pracę. Od czego zależy wielkość każdego z nich? Rozmiar stosu jest ustawiany podczas tworzenia wątku. Rozmiar sterty jest ustawiany podczas uruchamiania aplikacji, ale może rosnąć w miarę zapotrzebowania na miejsce (alokator żąda większej ilości pamięci od systemu operacyjnego). Co sprawia, że jest szybszy? Stos jest szybszy, ponieważ wzorzec dostępu sprawia, że alokowanie i zwalnianie pamięci z niego jest trywialne (wskaźnik / liczba całkowita jest po prostu zwiększana lub zmniejszana), podczas gdy sterta ma znacznie bardziej złożoną księgowość związaną z alokacją lub zwalnianiem. Ponadto każdy bajt w stosie jest bardzo często używany ponownie, co oznacza, że ma tendencję do mapowania do pamięci podręcznej procesora, dzięki czemu jest bardzo szybki. Innym spadkiem wydajności stosu jest to, że sterta, będąca głównie zasobem globalnym, zazwyczaj musi być bezpieczna dla wielowątkowości, tj. Każda alokacja i cofnięcie alokacji musi być - zazwyczaj - zsynchronizowane ze „wszystkimi” innymi dostępami do sterty w programie. Wyraźna demonstracja: Źródło obrazu: vikashazrati.wordpress.com | Stos: Przechowywane w pamięci RAM komputera, tak jak sterta. Zmienne utworzone na stosie wyjdą poza zakres i zostaną automatycznie cofnięte. Znacznie szybsza alokacja w porównaniu ze zmiennymi na stercie. Zaimplementowany z rzeczywistą strukturą danych stosu. Przechowuje dane lokalne, adresy zwrotne, używane do przekazywania parametrów. Może wystąpić przepełnienie stosu, gdy jest używane zbyt dużo stosu (głównie z nieskończonej lub zbyt głębokiej rekursji, bardzo dużych alokacji). Dane utworzone na stosie mogą być używane bez wskaźników. Używałbyś stosu, gdybyś dokładnie wiedział, ile danych musisz alokować przed kompilacją i nie jest on zbyt duży. Zwykle maksymalny rozmiar jest już określony podczas uruchamiania programu. Sterta: Przechowywane w pamięci RAM komputera, podobnie jak stos. W C ++ zmienne na stercie muszą być niszczone ręcznie i nigdy nie wykraczają poza zakres. Dane są uwalniane za pomocą funkcji delete, delete [] lub bezpłatnie. Wolniej alokować w porównaniu ze zmiennymi na stosie. Używany na żądanie do przydzielania bloku danych do wykorzystania przez program. Może wystąpić fragmentacja, gdy jest dużo alokacji i zwolnień. W C ++ lub C dane utworzone na stercie będą wskazywane przez wskaźniki i przydzielane odpowiednio za pomocą new lub malloc. Mogą wystąpić błędy alokacji, jeśli zażądano przydzielenia zbyt dużego bufora. Możesz użyć sterty, jeśli nie wiesz dokładnie, ile danych będziesz potrzebować w czasie wykonywania lub jeśli musisz przydzielić dużo danych. Odpowiedzialny za wycieki pamięci. Przykład: int foo () { char * pBuffer; // <- jeszcze nic nie zostało przydzielone (z wyjątkiem samego wskaźnika, który jest tutaj przydzielony na stosie). bool b = true; // Przydzielone na stosie. jeśli (b) { // Utwórz 500 bajtów na stosie bufor zwęglenia [500]; // Utwórz 500 bajtów na stercie pBuffer = nowy znak [500]; } // <- tu cofnięto przydział bufora, pBuffer nie } // <--- ojej, jest wyciek pamięci, powinienem był zadzwonić do delete [] pBuffer; | Najważniejsze jest to, że sterta i stos to ogólne terminy określające sposoby przydzielania pamięci. Można je realizować na wiele różnych sposobów, a terminy odnoszą się do podstawowych pojęć. W stosie przedmiotów przedmioty są umieszczane jeden na drugim w kolejności, w jakiej zostały tam umieszczone, i możesz usunąć tylko górny(bez przewracania całości). Prostota stosu polega na tym, że nie ma potrzeby utrzymywania tabeli zawierającej zapis każdej sekcji przydzielonej pamięci; jedyną potrzebną informacją o stanie jest pojedynczy wskaźnik do końca stosu. Aby przydzielić i cofnąć alokację, po prostu zwiększasz i zmniejszasz ten pojedynczy wskaźnik. Uwaga: czasami można zaimplementować stos tak, aby zaczynał się od góry sekcji pamięci i rozszerzał się w dół, zamiast rosnąć w górę. W stercie nie ma określonej kolejności umieszczania przedmiotów. Możesz sięgać i usuwać elementy w dowolnej kolejności, ponieważ nie ma wyraźnego elementu „z góry”. Alokacja sterty wymaga utrzymywania pełnego zapisu tego, jaka pamięć jest przydzielona, a co nie, a także konserwacji narzutów w celu zmniejszenia fragmentacji, znalezienia ciągłych segmentów pamięci wystarczająco dużych, aby pasowały do żądanego rozmiaru i tak dalej. Pamięć można cofnąć w dowolnym momencie, pozostawiając wolne miejsce. Czasami alokator pamięci wykonuje zadania konserwacyjne, takie jak defragmentacja pamięci przez przenoszenie przydzielonej pamięci lub zbieranie elementów bezużytecznych - identyfikowanie w czasie wykonywania, kiedy pamięć nie znajduje się już w zakresie i zwalnianie jej. Obrazy te powinny całkiem nieźle opisywać dwa sposoby przydzielania i zwalniania pamięci w stosie i stercie. Mniam! W jakim stopniu są one kontrolowane przez system operacyjny lub środowisko wykonawcze języka? Jak wspomniano, sterta i stos są terminami ogólnymi i można je zaimplementować na wiele sposobów. Programy komputerowe zazwyczaj mają stos zwany stosem wywołań, który przechowuje informacje istotne dla bieżącej funkcji, takie jak wskaźnik do dowolnej funkcji, z której został wywołany, oraz wszelkie zmienne lokalne. Ponieważ funkcje wywołują inne funkcje, a następnie zwracają, stos rośnie i kurczy się, aby przechowywać informacje z funkcji znajdujących się dalej w stosie wywołań. Program tak naprawdę nie ma nad nim kontroli w czasie wykonywania; zależy to od języka programowania, systemu operacyjnego, a nawet architektury systemu. Sterta to ogólny termin używany do określenia dowolnej pamięci przydzielanej dynamicznie i losowo; tj. nie działa. Pamięć jest zwykle przydzielana przez system operacyjny, a aplikacja wywołuje funkcje API w celu wykonania tej alokacji. Zarządzanie dynamicznie alokowaną pamięcią wymaga sporego narzutu, który jest zwykle obsługiwany przez kod wykonawczy używanego języka programowania lub środowiska. Jaki jest ich zakres? Stos wywołań jest koncepcją tak niskiego poziomu, że nie odnosi się do „zakresu” w sensie programowania. Jeśli zdemontujesz jakiś kod, zobaczysz odniesienia w stylu względnego wskaźnika do części stosu, ale jeśli chodzi o język wyższego poziomu, język narzuca własne reguły zakresu. Jednak jednym z ważnych aspektów stosu jest to, że po powrocie funkcji wszystko, co jest lokalne dla tej funkcji, jest natychmiast zwalniane ze stosu. To działa tak, jak można by się tego spodziewać, biorąc pod uwagę sposób działania języków programowania. W kupie jest to również trudne do zdefiniowania. Zasięg to wszystko, co jest ujawniane przez system operacyjny, ale twój język programowania prawdopodobnie dodaje swoje reguły dotyczące tego, czym jest „zakres” w twojej aplikacji. Architektura procesora i system operacyjny używają adresowania wirtualnego, które procesor tłumaczy na adresy fizyczne i występują błędy stron itp. Śledzą, które strony należą do jakich aplikacji. Jednak nigdy nie musisz się tym martwić, ponieważ po prostu używasz dowolnej metody używanej przez twój język programowania do przydzielania i zwalniania pamięci i sprawdzania błędów (jeśli alokacja / zwolnienie nie powiedzie się z jakiegokolwiek powodu). Od czego zależy wielkość każdego z nich? Znowu zależy to od języka, kompilatora, systemu operacyjnego i architektury. Stos jest zwykle wstępnie alokowany, ponieważ z definicji musi być pamięcią ciągłą. Kompilator języka lub system operacyjny określają jego rozmiar. Nie przechowujesz dużych porcji danych na stosie, więc będzie on na tyle duży, że nigdy nie powinien być w pełni używany, z wyjątkiem przypadków niechcianej niekończącej się rekursji (stąd „przepełnienie stosu”) lub innych nietypowych decyzji programistycznych. Sterta to ogólny termin określający wszystko, co można dynamicznie alokować. W zależności od tego, jak na to patrzysz, stale się zmienia. W nowoczesnych procesorach i systemach operacyjnych dokładny sposób działania jest i tak bardzo abstrakcyjny, więc zwykle nie musisz się martwić, jak to działa w głębi, z wyjątkiem tego (w językach, w których pozwala) nie możesz używać pamięci nie przydzieliłeś jeszcze ani pamięci, którą zwolniłeś. Co sprawia, że jest szybszy? Stos jest szybszy, ponieważ cała wolna pamięć jest zawsze ciągła. Nie ma potrzeby utrzymywania listy wszystkich segmentów wolnej pamięci, wystarczy jeden wskaźnik do bieżącego szczytu stosu. Kompilatory zwykle przechowują ten wskaźnik w specjalnym, szybkim rejestrze do tego celu. Co więcej, kolejne operacje na stosie są zwykle skoncentrowane w bardzo bliskich obszarach pamięci, co na bardzo niskim poziomie jest dobre do optymalizacji przez procesor wbudowanypamięci podręczne. | (Przeniosłem tę odpowiedź z innego pytania, które było mniej więcej naśladownictwem tego.) Odpowiedź na twoje pytanie jest specyficzna dla implementacji i może się różnić w zależności od kompilatorów i architektur procesorów. Jednak tutaj jest uproszczone wyjaśnienie. Zarówno stos, jak i sterta są obszarami pamięci przydzielonymi przez podstawowy system operacyjny (często pamięć wirtualną mapowaną na pamięć fizyczną na żądanie). W środowisku wielowątkowym każdy wątek będzie miał swój własny, całkowicie niezależny stos, ale będzie współdzielić stertę. Jednoczesny dostęp musi być kontrolowany na stercie i nie jest możliwy na stosie. Kupa Sterta zawiera połączoną listę używanych i wolnych bloków. Nowe alokacje na stercie (przez new lub malloc) są realizowane przez utworzenie odpowiedniego bloku z jednego z wolnych bloków. Wymaga to zaktualizowania listy bloków na stercie. Te metainformacje o blokach na stercie są również przechowywane na stercie często na małym obszarze tuż przed każdym blokiem. W miarę wzrostu stosu nowe bloki są często przydzielane z niższych adresów do wyższych adresów. W ten sposób można myśleć o stercie jako o stercie bloków pamięci, które powiększają się w miarę przydzielania pamięci. Jeśli sterta jest zbyt mała dla alokacji, rozmiar można często zwiększyć, uzyskując więcej pamięci z bazowego systemu operacyjnego. Przydzielanie i zwalnianie wielu małych bloków może pozostawić stertę w stanie, w którym istnieje wiele małych wolnych bloków rozmieszczonych między używanymi blokami. Żądanie przydzielenia dużego bloku może się nie powieść, ponieważ żaden z wolnych bloków nie jest wystarczająco duży, aby spełnić żądanie alokacji, nawet jeśli łączny rozmiar wolnych bloków może być wystarczająco duży. Nazywa się to fragmentacją sterty. Gdy używany blok, który sąsiaduje z wolnym blokiem, zostaje zwolniony, nowy wolny blok może zostać połączony z sąsiednim wolnym blokiem, aby utworzyć większy wolny blok, skutecznie zmniejszając fragmentację sterty. Stos Stos często działa w ścisłym tandemie ze specjalnym rejestrem procesora zwanym wskaźnikiem stosu. Początkowo wskaźnik stosu wskazuje wierzchołek stosu (najwyższy adres na stosie). Procesor ma specjalne instrukcje dotyczące umieszczania wartości na stosie i usuwania ich ze stosu. Każde wypchnięcie przechowuje wartość w bieżącej lokalizacji wskaźnika stosu i zmniejsza wskaźnik stosu. Pop pobiera wartość wskazywaną przez wskaźnik stosu, a następnie zwiększa wskaźnik stosu (nie daj się zmylić faktem, że dodanie wartości do stosu zmniejsza wskaźnik stosu, a usunięcie wartości zwiększa go. Pamiętaj, że stos rośnie do dół). Wartości przechowywane i pobierane są wartościami rejestrów procesora. Gdy funkcja jest wywoływana, procesor wykorzystuje specjalne instrukcje, które przekazują bieżący wskaźnik instrukcji, tj. Adres kodu wykonywanego na stosie. Następnie CPU przechodzi do funkcji, ustawiając wskaźnik instrukcji na adres wywoływanej funkcji. Później, gdy funkcja zwraca, stary wskaźnik instrukcji jest zdejmowany ze stosu i wykonywanie jest wznawiane w kodzie zaraz po wywołaniu funkcji. Po wprowadzeniu funkcji wskaźnik stosu jest zmniejszany, aby przydzielić więcej miejsca na stosie dla zmiennych lokalnych (automatycznych). Jeśli funkcja ma jedną lokalną zmienną 32-bitową, cztery bajty są odkładane na stosie. Po powrocie funkcji wskaźnik stosu jest cofany, aby zwolnić przydzielony obszar. Jeśli funkcja ma parametry, są one umieszczane na stosie przed wywołaniem funkcji. Kod funkcji jest następnie w stanie przejść w górę stosu od bieżącego wskaźnika stosu, aby zlokalizować te wartości. Wywołania funkcji zagnieżdżania działają jak marzenie. Każde nowe wywołanie przydzieli parametry funkcji, adres zwrotny i miejsce na zmienne lokalne, a te rekordy aktywacji mogą być ułożone w stosy dla wywołań zagnieżdżonych i będą się rozwijać w prawidłowy sposób po powrocie funkcji. Ponieważ stos jest ograniczonym blokiem pamięci, możesz spowodować przepełnienie stosu, wywołując zbyt wiele funkcji zagnieżdżonych i / lub przydzielając zbyt dużo miejsca na zmienne lokalne. Często obszar pamięci używany dla stosu jest skonfigurowany w taki sposób, że pisanie poniżej dolnego (najniższego adresu) stosu wyzwala pułapkę lub wyjątek w CPU. Ten wyjątkowy stan może następnie zostać przechwycony przez środowisko wykonawcze i przekształcony w pewnego rodzaju wyjątek przepełnienia stosu. Czy funkcję można zaalokować na stercie zamiast na stosie? Nie, rekordy aktywacji funkcji (tj. Zmiennych lokalnych lub automatycznych) są przydzielane na stosie, który jest używany nie tylko do przechowywania tych zmiennych, ale także do śledzenia zagnieżdżonych wywołań funkcji. Sposób zarządzania stertą zależy tak naprawdę od środowiska wykonawczego. C używa malloc, a C ++ używa new, ale wiele innych języków ma czyszczenie pamięci. Jednak stos jest funkcją niskiego poziomu, ściśle związaną z architekturą procesora. Odtąd uprawianie pryzmy, gdy nie ma wystarczającej ilości miejsca, nie jest zbyt trudnemożna go zaimplementować w wywołaniu biblioteki, która obsługuje stertę. Jednak powiększanie stosu jest często niemożliwe, ponieważ przepełnienie stosu jest wykrywane tylko wtedy, gdy jest za późno; a zamknięcie wątku wykonania jest jedyną realną opcją. | W poniższym kodzie C # public void Method1 () { int i = 4; int y = 2; klasa1 cls1 = nowa klasa1 (); } Oto jak zarządzana jest pamięć Zmienne lokalne, które muszą trwać tylko tak długo, jak długo wywołanie funkcji znajduje się na stosie. Sterta jest używana dla zmiennych, których czasu życia tak naprawdę nie znamy z góry, ale oczekujemy, że będą trwać przez chwilę. W większości języków ważne jest, abyśmy wiedzieli w czasie kompilacji, jak duża jest zmienna, jeśli chcemy ją przechowywać na stosie. Obiekty (które różnią się rozmiarem w miarę ich aktualizowania) trafiają na stos, ponieważ w czasie tworzenia nie wiemy, jak długo będą trwać. W wielu językach sterta jest odśmiecana w celu znalezienia obiektów (takich jak obiekt cls1), które nie mają już żadnych odniesień. W Javie większość obiektów trafia bezpośrednio do sterty. W językach takich jak C / C ++ struktury i klasy mogą często pozostać na stosie, gdy nie masz do czynienia ze wskaźnikami. Więcej informacji można znaleźć tutaj: Różnica między alokacją pamięci stosu i sterty «timmurphy.org i tu: Tworzenie obiektów na stosie i stercie Ten artykuł jest źródłem powyższego obrazu: Sześć ważnych koncepcji .NET: stos, sterta, typy wartości, typy odwołań, opakowanie i rozpakowywanie - CodeProject ale pamiętaj, że może zawierać pewne nieścisłości. | Stos Kiedy wywołujesz funkcję, argumenty tej funkcji oraz inne narzuty są umieszczane na stosie. Niektóre informacje (np. Dokąd udać się po powrocie) są tam również przechowywane. Kiedy deklarujesz zmienną wewnątrz swojej funkcji, ta zmienna jest również alokowana na stosie. Zwalnianie stosu jest dość proste, ponieważ zawsze zwalniasz przydział w odwrotnej kolejności, w jakiej dokonujesz alokacji. Stosy są dodawane podczas wprowadzania funkcji, a odpowiadające im dane są usuwane, gdy je zamykasz. Oznacza to, że masz tendencję do pozostawania w małym obszarze stosu, chyba że wywołujesz wiele funkcji, które wywołują wiele innych funkcji (lub tworzysz rozwiązanie rekurencyjne). The Heap Sterta to ogólna nazwa miejsca, w którym umieszczasz dane, które tworzysz w locie. Jeśli nie wiesz, ile statków kosmicznych utworzy twój program, prawdopodobnie użyjesz nowego (lub malloc lub równoważnego) operatora do stworzenia każdego statku kosmicznego. Ta alokacja będzie trwać przez jakiś czas, więc prawdopodobnie uwolnimy rzeczy w innej kolejności niż je stworzyliśmy. Zatem sterta jest znacznie bardziej złożona, ponieważ w końcu istnieją obszary pamięci, które są nieużywane, przeplatane fragmentami, które są - pamięć ulega fragmentacji. Znalezienie wolnej pamięci o wymaganym rozmiarze jest trudnym problemem. Dlatego należy unikać stosu (choć nadal jest często używany). Realizacja Implementacja zarówno stosu, jak i sterty jest zwykle ograniczona do środowiska wykonawczego / systemu operacyjnego. Często gry i inne aplikacje, które mają krytyczne znaczenie dla wydajności, tworzą własne rozwiązania pamięciowe, które pobierają dużą część pamięci ze sterty, a następnie udostępniają ją wewnętrznie, aby uniknąć polegania na systemie operacyjnym jako pamięci. Jest to praktyczne tylko wtedy, gdy zużycie pamięci znacznie różni się od normy - np. W grach, w których ładujesz poziom w jednej ogromnej operacji i możesz wyrzucić wszystko w kolejnej ogromnej operacji. Fizyczna lokalizacja w pamięci Jest to mniej istotne niż myślisz z powodu technologii zwanej pamięcią wirtualną, która sprawia, że twój program myśli, że masz dostęp do określonego adresu, pod którym dane fizyczne znajdują się gdzie indziej (nawet na dysku twardym!). Adresy, które otrzymujesz dla stosu, są w kolejności rosnącej, gdy drzewo wywołań staje się głębsze. Adresy stosu są nieprzewidywalne (tj. Specyficzne dla implementacji) i szczerze mówiąc nie są ważne. | Dla wyjaśnienia, ta odpowiedź zawiera niepoprawne informacje (Thomas poprawił swoją odpowiedź po komentarzach, fajnie :)). Inne odpowiedzi po prostu nie wyjaśniają, co oznacza statyczna alokacja. Dlatego wyjaśnię poniżej trzy główne formy alokacji i sposób, w jaki zwykle odnoszą się one do sterty, stosu i segmentu danych poniżej. Pokażę również kilka przykładów w C / C ++ i Pythonie, aby pomóc ludziom zrozumieć. Zmienne „statyczne” (alokowane statycznie AKA) nie są alokowane na stosie. Nie zakładaj tego - wiele osób robi to tylko dlatego, że „statyczne” brzmi podobnie jak „stos”. W rzeczywistości nie istnieją ani na stosie, ani na stercie. Są częścią tak zwanego segmentu danych. Jednak ogólnie lepiej jest rozważyć „zakres” i „okres istnienia” niż „stos” i „stertę”. Zakres odnosi się do części kodu, które mają dostęp do zmiennej. Ogólnie myślimy o zasięgu lokalnym (do którego można uzyskać dostęp tylko za pomocą bieżącej funkcji) w porównaniu z zakresem globalnym (można uzyskać dostęp w dowolnym miejscu), chociaż zakres może być znacznie bardziej złożony. Okres istnienia odnosi się do sytuacji, gdy zmienna jest przydzielana i zwalniana podczas wykonywania programu. Zwykle myślimy o alokacji statycznej (zmiennabędzie trwać przez cały czas trwania programu, co czyni go użytecznym do przechowywania tych samych informacji w kilku wywołaniach funkcji) w porównaniu z automatycznym przydzielaniem (zmienna utrzymuje się tylko podczas pojedynczego wywołania funkcji, dzięki czemu jest przydatna do przechowywania informacji, funkcji i można je odrzucić po zakończeniu) w porównaniu z alokacją dynamiczną (zmienne, których czas trwania jest definiowany w czasie wykonywania, zamiast czasu kompilacji, jak statyczny lub automatyczny). Chociaż większość kompilatorów i interpreterów implementuje to zachowanie w podobny sposób, jeśli chodzi o używanie stosów, stert itp., Kompilator może czasami złamać te konwencje, jeśli chce, o ile zachowanie jest poprawne. Na przykład, z powodu optymalizacji, zmienna lokalna może istnieć tylko w rejestrze lub zostać całkowicie usunięta, mimo że większość zmiennych lokalnych istnieje na stosie. Jak wskazano w kilku komentarzach, możesz swobodnie zaimplementować kompilator, który nawet nie używa stosu lub sterty, ale zamiast tego inne mechanizmy przechowywania (rzadko się to robi, ponieważ stosy i sterty są do tego świetne). Aby zilustrować to wszystko, przedstawię prosty kod w języku C z adnotacjami. Najlepszym sposobem nauki jest uruchomienie programu w debugerze i obserwowanie jego zachowania. Jeśli wolisz czytać Pythona, przejdź do końca odpowiedzi :) // Statycznie przydzielane w segmencie danych podczas pierwszego ładowania programu / biblioteki DLL // Zwolniony, gdy program / DLL kończy pracę // zakres - można uzyskać do niego dostęp z dowolnego miejsca w kodzie int someGlobalVariable; // Statycznie przydzielane w segmencie danych podczas pierwszego ładowania programu // Zwolniony, gdy program / DLL kończy pracę // zakres - można uzyskać do niego dostęp z dowolnego miejsca w tym konkretnym pliku kodu static int someStaticVariable; // "someArgument" jest przydzielane na stosie za każdym razem, gdy wywoływana jest funkcja MyFunction // „someArgument” jest zwalniany, gdy funkcja MyFunction zwraca // zakres - można uzyskać do niego dostęp tylko w MyFunction () void MyFunction (int someArgument) { // Statycznie przydzielane w segmencie danych podczas pierwszego ładowania programu // Zwolniony, gdy program / DLL kończy pracę // zakres - można uzyskać do niego dostęp tylko w MyFunction () static int someLocalStaticVariable; // Przydzielane na stosie za każdym razem, gdy wywoływana jest funkcja MyFunction // Zwolnione, gdy funkcja MyFunction zwraca // zakres - można uzyskać do niego dostęp tylko w MyFunction () int someLocalVariable; // * Wskaźnik * jest przydzielany na stosie za każdym razem, gdy wywoływana jest funkcja MyFunction // Ten wskaźnik jest zwalniany, gdy funkcja MyFunction zwraca // zasięg - do wskaźnika można się dostać tylko w MyFunction () int * someDynamicVariable; // Ten wiersz powoduje przydzielenie miejsca na liczbę całkowitą w stercie // kiedy ta linia jest wykonywana. Zauważ, że to nie jest na początku // wywołanie MyFunction (), podobnie jak zmienne automatyczne // zasięg - tylko kod w MyFunction () może uzyskać dostęp do tej przestrzeni // * poprzez tę konkretną zmienną *. // Jeśli jednak przekażesz adres gdzie indziej, ten kod // też ma do niego dostęp someDynamicVariable = new int; // Ten wiersz zwalnia miejsce na liczbę całkowitą w stercie. // Gdybyśmy tego nie napisali, pamięć zostałaby „wyciekła”. // Zwróć uwagę na podstawową różnicę między stosem a stertą // sterta musi być zarządzana. Stos jest zarządzany za nas. usuń someDynamicVariable; // W innych przypadkach, zamiast cofać alokację tego miejsca na stosie, ty // może przechowywać adres w bardziej trwałym miejscu do późniejszego wykorzystania. // Niektóre języki zajmują się nawet zwalnianiem za Ciebie ... ale // zawsze musi być załatwiony w czasie wykonywania przez jakiś mechanizm. // Kiedy funkcja zwraca, someArgument, someLocalVariable // i wskaźnik someDynamicVariable zostaną cofnięte. // Przestrzeń wskazywana przez zmienną dynamiczną już była // cofnięto przydział przed zwrotem. powrót; } // Zwróć uwagę, że someGlobalVariable, someStaticVariable i // NiektóreLocalStaticVariable nadal istnieją i nie są // cofnięte do czasu zakończenia programu. Szczególnie przejmującym przykładem tego, dlaczego ważne jest rozróżnienie między okresem istnienia a zakresem, jest to, że zmienna może mieć zasięg lokalny, ale statyczny okres istnienia - na przykład „someLocalStaticVariable” w przykładzie powyżej. Takie zmienne mogą sprawić, że nasze wspólne, ale nieformalne nawyki nazywania będą bardzo zagmatwane. Na przykład, kiedy mówimy „lokalnie”, zwykle mamy na myśli „zmienną automatycznie przydzielaną lokalnie o zasięgu lokalnym”, a kiedy mówimy o zasięgu globalnym, mamy na myśli „zmienną przydzieloną statycznie o zasięgu globalnym”. Niestety, jeśli chodzi o rzeczy takie jak „zmienne przydzielane statycznie w zakresie plików”, wiele osób po prostu mówi… „huh ???”. Niektóre opcje składniowe w C / C ++ zaostrzają ten problem - na przykład wiele osób uważa, że zmienne globalne nie są „statyczne” z powodu składni pokazanej poniżej. int var1; // Ma zasięg globalny i statyczną alokację static int var2; // Ma zasięg pliku i statyczną alokację int main () {return 0;} Zauważ, że umieszczenie słowa kluczowego „static” w powyższej deklaracji uniemożliwia var2 uzyskanie zasięgu globalnego. Niemniej jednak globalna zmienna var1 ma statyczną alokację. To nie jestintuicyjny! Z tego powodu staram się nigdy nie używać słowa „statyczny” przy opisywaniu zakresu i zamiast tego mówię coś w rodzaju „zakres plikowy” lub „ograniczony do pliku”. Jednak wiele osób używa wyrażenia „statyczny” lub „zakres statyczny” do opisania zmiennej, do której można uzyskać dostęp tylko z jednego pliku kodu. W kontekście cyklu życia „statyczna” zawsze oznacza, że zmienna jest przydzielana na początku programu i zwalniana, gdy program kończy. Niektórzy uważają te koncepcje za specyficzne dla C / C ++. Oni nie są. Na przykład poniższy przykład Pythona ilustruje wszystkie trzy typy alokacji (istnieją pewne subtelne różnice możliwe w językach interpretowanych, których nie będę tutaj omawiać). z importu datetime klasa Animal: _FavoriteFood = 'Undefined' # _FavoriteFood jest przydzielana statycznie def PetAnimal (ja): curTime = datetime.time (datetime.now ()) # curTime jest przydzielane automatycznie print ("Dziękuję za głaskanie mnie. Ale to" + str (curTime) + ", powinieneś mnie nakarmić. Moje ulubione jedzenie to" + self._FavoriteFood) klasa Kot (Zwierzę): _FavoriteFood = 'tuna' # Uwaga, odkąd nadpisujemy, klasa Cat ma własną statycznie przydzieloną zmienną _FavoriteFood, inną niż Animal klasa Pies (Zwierzę): _FavoriteFood = 'steak' # Podobnie klasa Dog otrzymuje własną zmienną statyczną. Ważna uwaga - ta jedna zmienna statyczna jest wspólna dla wszystkich instancji Dog, dlatego nie jest dynamiczna! if __name__ == "__main__": wąsy = Cat () # Przydzielane dynamicznie fido = Dog () # Przydzielane dynamicznie rinTinTin = Dog () # Przydzielane dynamicznie wąsy.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () Dog._FavoriteFood = 'milkbones' wąsy.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () # Wynik to: # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.255000, powinieneś mnie nakarmić. Moim ulubionym jedzeniem jest tuńczyk # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.255000, powinieneś mnie nakarmić. Moim ulubionym jedzeniem jest stek # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.255000, powinieneś mnie nakarmić. Moim ulubionym jedzeniem jest stek # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.255000, powinieneś mnie nakarmić. Moim ulubionym jedzeniem jest tuńczyk # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.255000, powinieneś mnie nakarmić. Moje ulubione jedzenie to milkbones # Dziękuję za głaskanie mnie. Ale jest 13: 05: 02.256000, powinieneś mnie nakarmić. Moje ulubione jedzenie to milkbones | Inni całkiem dobrze odpowiedzieli na szerokie pociągnięcia, więc podam kilka szczegółów. Stos i sterta nie muszą być pojedyncze. Typową sytuacją, w której masz więcej niż jeden stos, jest sytuacja, gdy w procesie jest więcej niż jeden wątek. W tym przypadku każdy wątek ma swój własny stos. Możesz również mieć więcej niż jedną stertę, na przykład niektóre konfiguracje DLL mogą powodować przydzielanie różnych bibliotek DLL z różnych stert, dlatego ogólnie złym pomysłem jest zwolnienie pamięci przydzielonej przez inną bibliotekę. W języku C można uzyskać korzyść z alokacji o zmiennej długości dzięki zastosowaniu metody przydzielania, która alokuje na stosie, w przeciwieństwie do alokacji, która alokuje na stercie. Ta pamięć nie przetrwa instrukcji return, ale jest przydatna dla bufora magazynującego. Tworzenie ogromnego tymczasowego bufora w systemie Windows, którego nie używasz zbyt często, nie jest darmowe. Dzieje się tak, ponieważ kompilator wygeneruje pętlę sondy stosu, która jest wywoływana za każdym razem, gdy funkcja jest wprowadzana, aby upewnić się, że stos istnieje (ponieważ system Windows używa pojedynczej strony ochronnej na końcu stosu, aby wykryć, kiedy musi powiększyć stos. Jeśli uzyskasz dostęp do pamięci więcej niż jedną stronę poza końcem stosu, nastąpi awaria). Przykład: void myfunction () { char duży [10000000]; // Zrób coś, co wykorzystuje tylko przez pierwszy 1K dużego 99% czasu. } | Inni odpowiedzieli bezpośrednio na twoje pytanie, ale myślę, że próbując zrozumieć stos i stertę, pomocne jest rozważenie układu pamięci tradycyjnego procesu UNIX (bez wątków i alokatorów opartych na mmap ()). Na stronie internetowej Glosariusz zarządzania pamięcią znajduje się schemat tego układu pamięci. Stos i sterta są tradycyjnie umieszczane na przeciwnych końcach wirtualnej przestrzeni adresowej procesu. Stos rośnie automatycznie po uzyskaniu do niego dostępu, aż do rozmiaru ustawionego przez jądro (który można dostosować za pomocą setrlimit (RLIMIT_STACK, ...)). Sterta rośnie, gdy alokator pamięci wywołuje wywołanie systemowe brk () lub sbrk (), mapując więcej stron pamięci fizycznej na wirtualną przestrzeń adresową procesu. W systemach bez pamięci wirtualnej, takich jak niektóre systemy wbudowane, często stosuje się ten sam podstawowy układ, z wyjątkiem tego, że stos i sterta mają stały rozmiar. Jednak w innych systemach wbudowanych (takich jak te oparte na mikrokontrolerach Microchip PIC) stos programu jest oddzielnym blokiem pamięci, który nie jest adresowalny przez instrukcje przenoszenia danych i może być modyfikowany lub odczytywany tylko pośrednio poprzez instrukcje przepływu programu powrót itp.). Inne architektury, takie jak procesory Intel Itanium, mają wiele stosów. W tym sensie stos jest elementem architektury procesora. | Stos jest częściąpamięci, którą można manipulować za pomocą kilku kluczowych instrukcji języka asemblera, takich jak 'pop' (usuwanie i zwracanie wartości ze stosu) i 'push' (przesyłanie wartości na stos), ale także wywołanie odkłada adres, aby powrócić do stosu) i return (powrót z podprogramu - to zdejmuje adres ze stosu i przeskakuje do niego). Jest to obszar pamięci poniżej rejestru wskaźnika stosu, który można ustawić w razie potrzeby. Stos jest również używany do przekazywania argumentów do podprogramów, a także do zachowywania wartości w rejestrach przed wywołaniem podprogramów. Sterta to część pamięci, która jest przekazywana aplikacji przez system operacyjny, zwykle za pośrednictwem wywołania systemowego, takiego jak malloc. W nowoczesnych systemach operacyjnych ta pamięć to zestaw stron, do których ma dostęp tylko proces wywołujący. Rozmiar stosu jest określany w czasie wykonywania i zazwyczaj nie zwiększa się po uruchomieniu programu. W programie C stos musi być wystarczająco duży, aby pomieścić każdą zmienną zadeklarowaną w każdej funkcji. Sterta będzie rosła dynamicznie w razie potrzeby, ale system operacyjny ostatecznie wykonuje wywołanie (często będzie powiększał stertę o więcej niż wartość żądana przez malloc, więc przynajmniej niektóre przyszłe malloc nie będą musiały wracać do jądra do uzyskać więcej pamięci. To zachowanie często można dostosować) Ponieważ stos został przydzielony przed uruchomieniem programu, nigdy nie trzeba wykonywać malloc, zanim będzie można go użyć, więc jest to niewielka zaleta. W praktyce bardzo trudno jest przewidzieć, co będzie szybkie, a co wolne w nowoczesnych systemach operacyjnych z podsystemami pamięci wirtualnej, ponieważ sposób implementacji stron i miejsce ich przechowywania to szczegół implementacyjny. | Co to jest stos? Stos to stos przedmiotów, zazwyczaj starannie ułożonych. Stosy w architekturach komputerowych to obszary pamięci, w których dane są dodawane lub usuwane w sposób „ostatni na wejściu, pierwszy na wyjściu”. W aplikacji wielowątkowej każdy wątek będzie miał własny stos. Co to jest kupa? Sterta to nieporządny zbiór rzeczy, które zostały przypadkowo ułożone. W architekturach komputerowych sterta to obszar dynamicznie przydzielanej pamięci, który jest zarządzany automatycznie przez system operacyjny lub bibliotekę menedżera pamięci. Pamięć na stercie jest regularnie przydzielana, zwalniana i zmieniana jej wielkość podczas wykonywania programu, co może prowadzić do problemu zwanego fragmentacją. Fragmentacja występuje, gdy obiekty pamięci są przydzielane z małymi odstępami między nimi, które są zbyt małe, aby pomieścić dodatkowe obiekty pamięci. Wynik netto to procent miejsca na stercie, którego nie można użyć do dalszych alokacji pamięci. Oba razem W aplikacji wielowątkowej każdy wątek będzie miał własny stos. Ale wszystkie różne wątki będą współdzielić stertę. Ponieważ różne wątki współużytkują stertę w aplikacji wielowątkowej, oznacza to również, że musi istnieć pewna koordynacja między wątkami, aby nie próbowały uzyskać dostępu i manipulować tym samym fragmentem pamięci w stercie w o tym samym czasie. Co jest szybsze - stos czy sterta? I dlaczego? Stos jest znacznie szybszy niż sterta. Dzieje się tak z powodu sposobu alokacji pamięci na stosie. Przydzielanie pamięci na stosie jest tak proste, jak przesuwanie wskaźnika stosu w górę. Dla osób początkujących w programowaniu prawdopodobnie dobrym pomysłem jest skorzystanie ze stosu, ponieważ jest to łatwiejsze. Ponieważ stos jest mały, chciałbyś go użyć, gdy wiesz dokładnie, ile pamięci będziesz potrzebować na swoje dane lub jeśli wiesz, że rozmiar danych jest bardzo mały. Lepiej jest używać sterty, gdy wiesz, że będziesz potrzebować dużo pamięci na dane lub po prostu nie masz pewności, ile pamięci będziesz potrzebować (jak w przypadku tablicy dynamicznej). Model pamięci Java Stos to obszar pamięci, w którym przechowywane są zmienne lokalne (w tym parametry metod). Jeśli chodzi o zmienne obiektowe, są to jedynie odniesienia (wskaźniki) do rzeczywistych obiektów na stercie. Za każdym razem, gdy tworzona jest instancja obiektu, część pamięci sterty jest odkładana na bok, aby przechowywać dane (stan) tego obiektu. Ponieważ obiekty mogą zawierać inne obiekty, niektóre z tych danych mogą w rzeczywistości zawierać odniesienia do tych zagnieżdżonych obiektów. | Myślę, że wiele innych osób udzieliło Ci w większości poprawnych odpowiedzi w tej sprawie. Brakuje jednak jednego szczegółu, który polega na tym, że „sterta” powinna w rzeczywistości prawdopodobnie nazywać się „darmowym magazynem”. Przyczyną tego rozróżnienia jest to, że oryginalny bezpłatny magazyn został zaimplementowany ze strukturą danych znaną jako „sterta dwumianowa”. Z tego powodu alokacja z wczesnych implementacji malloc () / free () była alokacją ze sterty. Jednak w dzisiejszych czasach większość darmowych sklepów jest zaimplementowanych z bardzo skomplikowanymi strukturami danych, które nie są stertami dwumianowymi. | Ze stosem możesz zrobić kilka interesujących rzeczy. Na przykład masz funkcje takie jak przydzielanie (zakładając, że możesz ominąć liczne ostrzeżenia dotyczące jego użycia), które są formą malloc, któraw szczególności używa jako pamięci stosu, a nie sterty. To powiedziawszy, błędy pamięci oparte na stosie są jednymi z najgorszych, jakich doświadczyłem. Jeśli używasz pamięci sterty i przekroczysz granice przydzielonego bloku, masz przyzwoitą szansę na wywołanie błędu segmentu. (Nie w 100%: twój blok może przypadkowo sąsiadować z innym, który wcześniej zaalokowałeś.) Ale ponieważ zmienne utworzone na stosie są zawsze ze sobą sąsiadujące, wypisywanie poza granicami może zmienić wartość innej zmiennej. Dowiedziałem się, że ilekroć czuję, że mój program przestał przestrzegać praw logiki, prawdopodobnie jest to przepełnienie bufora. | Po prostu stos jest miejscem, w którym tworzone są zmienne lokalne. Ponadto za każdym razem, gdy wywołujesz podprogram, licznik programu (wskaźnik do następnej instrukcji maszynowej) i wszystkie ważne rejestry, a czasami parametry są umieszczane na stosie. Następnie wszelkie zmienne lokalne wewnątrz podprogramu są wypychane na stos (i stamtąd używane). Po zakończeniu podprogramu wszystkie te elementy są usuwane ze stosu. Dane z komputera i rejestru są pobierane i umieszczane z powrotem tam, gdzie były, więc program może działać wesoło. Sterta to obszar pamięci, z którego wykonywane są dynamiczne alokacje pamięci (jawne wywołania „new” lub „przydziel”). Jest to specjalna struktura danych, która umożliwia śledzenie bloków pamięci o różnych rozmiarach i stanu ich alokacji. W „klasycznych” systemach pamięć RAM była ułożona w taki sposób, że wskaźnik stosu zaczynał się od dołu pamięci, a wskaźnik stosu zaczynał się od góry i rosły ku sobie. Jeśli się nakładają, brakuje pamięci RAM. To jednak nie działa w przypadku nowoczesnych wielowątkowych systemów operacyjnych. Każdy wątek musi mieć własny stos, a te można tworzyć dynamicznie. | Z WikiAnwser. Stos Kiedy funkcja lub metoda wywołuje inną funkcję, która z kolei wywołuje inną funkcję, itd., Wykonywanie wszystkich tych funkcji pozostaje zawieszone, dopóki ostatnia funkcja nie zwróci jej wartości. Ten łańcuch zawieszonych wywołań funkcji jest stosem, ponieważ elementy na stosie (wywołania funkcji) zależą od siebie. Stos jest ważny do rozważenia podczas obsługi wyjątków i wykonywania wątków. Sterta Sterta to po prostu pamięć używana przez programy do przechowywania zmiennych. Elementy sterty (zmienne) nie mają ze sobą żadnych zależności i zawsze można uzyskać do nich losowy dostęp w dowolnym momencie. | Stos Bardzo szybki dojazd Nie musisz jawnie usuwać zmiennych Przestrzeń jest efektywnie zarządzana przez procesor, pamięć nie zostanie pofragmentowana Tylko zmienne lokalne Limit rozmiaru stosu (zależny od systemu operacyjnego) Nie można zmieniać rozmiaru zmiennych Sterta Dostęp do zmiennych można uzyskać globalnie Brak ograniczeń rozmiaru pamięci (Relatywnie) wolniejszy dostęp Brak gwarancji efektywnego wykorzystania miejsca, pamięć może z czasem ulec fragmentacji w miarę przydzielania, a następnie zwalniania bloków pamięci Musisz zarządzać pamięcią (odpowiadasz za przydzielanie i zwalnianie zmiennych) Zmienne można zmieniać za pomocą realloc () | W skrócie Stos jest używany do statycznej alokacji pamięci i sterta do dynamicznej alokacji pamięci, oba są przechowywane w pamięci RAM komputera. Szczegółowo Stos Stos jest strukturą danych „LIFO” (ostatnie weszło, pierwsze wyszło), która jest zarządzana i optymalizowana przez procesor dość ściśle. Za każdym razem, gdy funkcja deklaruje nową zmienną, jest ona „wypychana” na stos. Następnie za każdym razem, gdy funkcja kończy działanie, wszystkie zmienne umieszczone na stosie przez tę funkcję są zwalniane (to znaczy są usuwane). Po zwolnieniu zmiennej stosu ten obszar pamięci staje się dostępny dla innych zmiennych stosu. Zaletą używania stosu do przechowywania zmiennych jest to, że pamięć jest zarządzana za Ciebie. Nie musisz ręcznie przydzielać pamięci ani zwolnić jej, gdy nie jest już potrzebna. Co więcej, ponieważ procesor tak efektywnie organizuje pamięć stosu, odczyt i zapis do zmiennych stosu jest bardzo szybki. Więcej można znaleźć tutaj. The Heap Sterta to obszar pamięci komputera, który nie jest zarządzany automatycznie i nie jest tak ściśle zarządzany przez procesor. Jest to bardziej swobodny obszar pamięci (i jest większy). Aby przydzielić pamięć na stercie, musisz użyć funkcji malloc () lub calloc (), które są wbudowanymi funkcjami języka C. Po przydzieleniu pamięci na stercie jesteś odpowiedzialny za użycie funkcji free () w celu zwolnienia tej pamięci, gdy już jej nie potrzebujesz. Jeśli tego nie zrobisz, w programie wystąpi tzw. Wyciek pamięci. Oznacza to, że pamięć na stercie nadal będzie odkładana (i nie będzie dostępna dla innych procesów). Jak zobaczymy w sekcji debugowania, istnieje narzędzie o nazwie Valgrind, które może pomóc w wykryciu wycieków pamięci. W przeciwieństwie do stosu, sterta nie ma ograniczeń rozmiaru dla zmiennej wielkości (poza oczywistymi fizycznymi ograniczeniami twojego komputera). Czytanie i zapisywanie do pamięci sterty jest nieco wolniejsze, ponieważ trzeba używać wskaźników, aby uzyskać dostęp do pamięci na stercie. Wkrótce porozmawiamy o wskazówkach. W przeciwieństwie do stosu,zmienne utworzone na stercie są dostępne dla dowolnej funkcji w dowolnym miejscu programu. Zmienne sterty mają zasadniczo zasięg globalny. Więcej można znaleźć tutaj. Zmienne alokowane na stosie są zapisywane bezpośrednio do pamięci i dostęp do tej pamięci jest bardzo szybki, a ich alokacją zajmuje się podczas kompilacji programu. Kiedy funkcja lub metoda wywołuje inną funkcję, która z kolei wywołuje inną funkcję, itd., Wykonywanie wszystkich tych funkcji pozostaje zawieszone, dopóki ostatnia funkcja nie zwróci jej wartości. Stos jest zawsze rezerwowany w kolejności LIFO, ostatnio zarezerwowany blok jest zawsze następnym blokiem do zwolnienia. To sprawia, że śledzenie stosu jest naprawdę proste, a zwolnienie bloku ze stosu to nic innego jak dostosowanie jednego wskaźnika. Pamięć dla zmiennych alokowanych na stercie jest przydzielana w czasie wykonywania, a dostęp do tej pamięci jest nieco wolniejszy, ale rozmiar sterty jest ograniczony tylko wielkością pamięci wirtualnej. Elementy sterty nie mają ze sobą żadnych zależności i zawsze można uzyskać do nich losowy dostęp w dowolnym momencie. Możesz przydzielić blok w dowolnym momencie i zwolnić go w dowolnym momencie. To sprawia, że śledzenie, które części sterty są przydzielone lub wolne w danym momencie, jest znacznie bardziej skomplikowane. Możesz użyć stosu, jeśli wiesz dokładnie, ile danych musisz zaalokować przed kompilacją i nie jest on zbyt duży. Możesz użyć sterty, jeśli nie wiesz dokładnie, ile danych będziesz potrzebować w czasie wykonywania lub jeśli musisz przydzielić dużo danych. W sytuacji wielowątkowej każdy wątek będzie miał swój własny, całkowicie niezależny stos, ale będzie współdzielił stertę. Stos jest specyficzny dla wątku, a sterta jest specyficzna dla aplikacji. Stos jest ważny do rozważenia podczas obsługi wyjątków i wykonywania wątków. Każdy wątek otrzymuje stos, podczas gdy zwykle jest tylko jedna sterta dla aplikacji (chociaż nie jest rzadkością posiadanie wielu stert dla różnych typów alokacji). W czasie wykonywania, jeśli aplikacja potrzebuje więcej sterty, może przydzielić pamięć z wolnej pamięci, a jeśli stos potrzebuje pamięci, może przydzielić pamięć z wolnej pamięci przydzielonej aplikacji. Nawet więcej szczegółów podano tutaj i tutaj. A teraz przejdź do odpowiedzi na twoje pytanie. W jakim stopniu są one kontrolowane przez system operacyjny lub środowisko wykonawcze języka? System operacyjny przydziela stos dla każdego wątku na poziomie systemu podczas tworzenia wątku. Zazwyczaj system operacyjny jest wywoływany przez środowisko wykonawcze języka w celu przydzielenia stosu aplikacji. Więcej można znaleźć tutaj. Jaki jest ich zakres? Już podane na górze. „Możesz użyć stosu, jeśli wiesz dokładnie, ile danych musisz przydzielić przed kompilacją i nie jest on zbyt duży. Możesz użyć sterty, jeśli nie wiesz dokładnie, ile danych będziesz potrzebować w czasie wykonywania lub jeśli musisz przydzielić dużo danych ”. Więcej można znaleźć tutaj. Od czego zależy wielkość każdego z nich? Rozmiar stosu jest ustawiany przez system operacyjny podczas tworzenia wątku. Rozmiar sterty jest ustawiany podczas uruchamiania aplikacji, ale może rosnąć w miarę zapotrzebowania na miejsce (alokator żąda więcej pamięci od systemu operacyjnego). Co sprawia, że jest szybszy? Alokacja stosu jest znacznie szybsza, ponieważ wszystko, co tak naprawdę robi, to przesuwanie wskaźnika stosu. Używając pul pamięci, można uzyskać porównywalną wydajność z alokacji sterty, ale wiąże się to z niewielką dodatkową złożonością i własnymi problemami. Ponadto stos i sterta to nie tylko kwestia wydajności; zawiera również wiele informacji na temat przewidywanego czasu życia obiektów. Szczegóły można znaleźć tutaj. | OK, prosto i krótko, oznaczają zamówione a nie zamówione ...! Stos: w elementach stosu rzeczy nakładają się na siebie, co oznacza, że przetwarzanie będzie szybsze i wydajniejsze! ... Więc zawsze istnieje indeks wskazujący konkretny element, również przetwarzanie będzie szybsze, istnieje również związek między elementami! ... Sterta: Brak kolejności, przetwarzanie będzie wolniejsze, a wartości są pomieszane razem bez określonego porządku lub indeksu ... są losowe i nie ma między nimi związku ... więc czas wykonania i użycia może się różnić ... Tworzę również poniższy obrazek, aby pokazać, jak mogą wyglądać: | stos, sterta i dane każdego procesu w pamięci wirtualnej: | W latach 80-tych UNIX rozmnażał się jak króliki, a duże firmy produkowały własne. Exxon miał jedną, podobnie jak dziesiątki marek, które przeszły do historii. Sposób ułożenia pamięci pozostawał w gestii wielu realizatorów. Typowy program w C został rozłożony płasko w pamięci z możliwość zwiększenia poprzez zmianę wartości brk (). Zazwyczaj HEAP był tuż poniżej tej wartości Brk a zwiększenie wartości brk zwiększyło ilość dostępnego sterty. Pojedynczy STACK był zwykle obszarem poniżej HEAP, który był traktem pamięci zawierające nic wartościowego aż do początku następnego stałego bloku pamięci. Ten następny blok był często KODEM, który można było nadpisać danymi stosu w jednym ze słynnych hacków swojej epoki. Jednym z typowych bloków pamięci był BSS (blok zerowywartości) który przypadkowo nie został wyzerowany w ofercie jednego producenta. Innym był DATA zawierający zainicjowane wartości, w tym łańcuchy i liczby. Trzecim był CODE zawierający CRT (środowisko wykonawcze C), elementy główne, funkcje i biblioteki. Pojawienie się pamięci wirtualnej w systemie UNIX zmienia wiele ograniczeń. Nie ma obiektywnego powodu, dla którego te bloki muszą być ciągłe, lub ustalony rozmiar lub zamówiony teraz w określony sposób. Oczywiście, zanim UNIX był Multics, który nie cierpiał z powodu tych ograniczeń. Oto schemat przedstawiający jeden z układów pamięci z tamtej epoki. | Kilka centów: myślę, że dobrze będzie narysować pamięć graficzną i prostszą: Strzałki - pokazują, gdzie rośnie stos i sterta, rozmiar stosu procesu ma limit, zdefiniowany w systemie operacyjnym, zwykle limity rozmiaru stosu wątków przez parametry w API tworzenia wątków. Sterta zwykle ogranicza maksymalny rozmiar pamięci wirtualnej procesu, na przykład dla 32 bitów 2-4 GB. Tak prosty sposób: sterta procesu jest ogólna dla procesu i wszystkich wątków wewnątrz, używając do alokacji pamięci w typowym przypadku z czymś takim jak malloc (). Stos jest szybką pamięcią do przechowywania w typowym przypadku wskaźników powrotu funkcji i zmiennych, przetwarzanych jako parametry w wywołaniu funkcji, lokalne zmienne funkcji. | Ponieważ niektóre odpowiedzi były dziurawe, mam zamiar wnieść swój wkład. Zaskakujące jest, że nikt nie wspomniał, że wiele (tj. Niezwiązanych z liczbą działających wątków na poziomie systemu operacyjnego) stosów wywołań można znaleźć nie tylko w egzotycznych językach (PostScript) lub platformach (Intel Itanium), ale także we włóknach, zielonych wątkach i niektóre implementacje programów. Włókna, zielone nici i rutyny są pod wieloma względami podobne, co prowadzi do wielu nieporozumień. Różnica między włóknami a zielonymi nitkami polega na tym, że te pierwsze wykorzystują wielozadaniowość kooperacyjną, podczas gdy druga może mieć charakter kooperacyjny lub wywłaszczeniowy (lub nawet oba). Aby zobaczyć rozróżnienie między włóknami a rdzeniami, zobacz tutaj. W każdym razie celem obu włókien, zielonych nici i programów jest posiadanie wielu funkcji wykonywanych jednocześnie, ale nie równolegle (zobacz to pytanie SO dla rozróżnienia) w pojedynczym wątku na poziomie systemu operacyjnego, przenosząc kontrolę między sobą. w zorganizowany sposób. Używając włókien, zielonych nici lub rdzeni, zwykle masz osobny stos na funkcję. (Technicznie rzecz biorąc, nie tylko stos, ale cały kontekst wykonywania przypada na funkcję. Co najważniejsze, rejestry procesora). zgodnie z logiką twojego programu. Kiedy funkcja dobiega końca, jej stos jest niszczony. Tak więc liczba i okresy życia stosów są dynamiczne i nie są określane przez liczbę wątków na poziomie systemu operacyjnego! Zauważ, że powiedziałem "zwykle mają osobny stos na funkcję". Istnieją zarówno stosy, jak i bez stosów implementacje Couroutines. Najbardziej godnymi uwagi implementacjami stosu C ++ są Boost.Coroutine i async / await Microsoft PPL. (Jednak funkcje wznawialne C ++ (znane również jako „async i await”), które zostały zaproponowane w C ++ 17, prawdopodobnie używają programów bez stosu.) Nadchodzi propozycja dotycząca włókien do standardowej biblioteki C ++. Istnieją również biblioteki innych firm. Zielone wątki są niezwykle popularne w językach takich jak Python i Ruby. | Mam coś do przekazania, chociaż główne punkty są już omówione. Stos Bardzo szybki dojazd. Przechowywane w pamięci RAM. W tym miejscu ładowane są wywołania funkcji wraz z przekazanymi zmiennymi lokalnymi i parametrami funkcji. Przestrzeń jest zwalniana automatycznie, gdy program wychodzi poza zakres. Przechowywane w pamięci sekwencyjnej. Sterta Powolny dostęp w porównaniu do stosu. Przechowywane w pamięci RAM. Tutaj przechowywane są dynamicznie tworzone zmienne, co wymaga późniejszego zwolnienia przydzielonej pamięci po użyciu. Przechowywane wszędzie tam, gdzie odbywa się alokacja pamięci, zawsze dostępne za pomocą wskaźnika. Ciekawa uwaga: Gdyby wywołania funkcji były przechowywane w stercie, spowodowałoby to 2 nieporządne punkty: Dzięki sekwencyjnemu przechowywaniu w stosie wykonanie jest szybsze. Przechowywanie w stercie spowodowałoby ogromne zużycie czasu, przez co cały program wykonywałby się wolniej. Gdyby funkcje były przechowywane w stercie (niechlujna pamięć wskazywana przez wskaźnik), nie byłoby możliwości powrotu do adresu wywołującego z powrotem (który daje stos z powodu sekwencyjnego przechowywania w pamięci). | Łał! Tak wiele odpowiedzi i nie wydaje mi się, żeby jedna z nich była poprawna ... 1) Gdzie i jakie one są (fizycznie w pamięci prawdziwego komputera)? Stos jest pamięcią, która zaczyna się od najwyższego adresu pamięci przydzielonego obrazowi programu, a następnie maleje wartość. Jest zarezerwowany dla wywoływanych parametrów funkcji i dla wszystkich zmiennych tymczasowych używanych w funkcjach. Istnieją dwa stosy: publiczny i prywatny. Sterta prywatna zaczyna się na granicy 16-bajtowej (dla programów 64-bitowych) lub granicy 8-bajtowej (dla programów 32-bitowych) po ostatnim bajcie kodu w programie, a następnie rośniewartość stamtąd. Jest również nazywany stertą domyślną. Jeśli sterta prywatna stanie się zbyt duża, pokryje się z obszarem stosu, podobnie jak stos zachodzi na stertę, jeśli stanie się zbyt duży. Ponieważ stos zaczyna się od wyższego adresu i schodzi w dół do niższego adresu, przy odpowiednim hakowaniu możesz sprawić, że stos będzie tak duży, że zajmie prywatny obszar sterty i zachodzi na obszar kodu. Sztuczka polega więc na tym, aby nakładać się na tyle obszaru kodu, aby można go było podłączyć do kodu. Jest to trochę trudne i ryzykujesz awarię programu, ale jest to łatwe i bardzo skuteczne. Sterta publiczna znajduje się we własnej przestrzeni pamięci poza przestrzenią obrazu programu. Jest to pamięć, która zostanie zassana na dysk twardy, jeśli zasoby pamięci staną się ograniczone. 2) W jakim stopniu są one kontrolowane przez system operacyjny lub środowisko wykonawcze języka? Stos jest kontrolowany przez programistę, sterta prywatna jest zarządzana przez system operacyjny, a sterta publiczna nie jest kontrolowana przez nikogo, ponieważ jest to usługa systemu operacyjnego - wykonujesz żądania i albo są one przyznawane, albo odrzucane. 2b) Jaki jest ich zakres? Wszystkie są globalne dla programu, ale ich zawartość może być prywatna, publiczna lub globalna. 2c) Od czego zależy wielkość każdego z nich? Rozmiar stosu i sterty prywatnej są określane przez opcje środowiska uruchomieniowego kompilatora. Sterta publiczna jest inicjowana w czasie wykonywania przy użyciu parametru rozmiaru. 2d) Co sprawia, że jest się szybszym? Nie są zaprojektowane tak, aby były szybkie, mają być użyteczne. Sposób ich wykorzystania przez programistę określa, czy są one „szybkie” czy „wolne” ODNIESIENIE: https://norasandler.com/2019/02/18/Write-a-Compiler-10.html https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate | Wiele odpowiedzi jest poprawnych jako koncepcje, ale musimy zauważyć, że sprzęt (tj. Mikroprocesor) potrzebuje stosu, aby umożliwić wywoływanie podprogramów (CALL w języku asemblera ...). (Faceci OOP będą nazywać to metodami) Na stosie zapisujesz adresy zwrotne i wywołanie → push / ret → pop jest zarządzane bezpośrednio sprzętowo. Możesz użyć stosu do przekazywania parametrów ... nawet jeśli jest wolniejszy niż przy użyciu rejestrów (powiedziałby guru mikroprocesora lub dobra książka o BIOS-ie z lat 80 ...) Bez stosu żaden mikroprocesor nie może działać. (nie możemy sobie wyobrazić programu, nawet w języku asemblera, bez podprogramów / funkcji) Bez stosu może. (Program w języku asemblera może działać bez, ponieważ sterta jest koncepcją systemu operacyjnego, tak jak malloc, to jest wywołanie OS / Lib. Wykorzystanie stosu jest szybsze, ponieważ: Czy sprzęt, a nawet push / pop są bardzo wydajne. malloc wymaga wejścia w tryb jądra, użycia blokady / semafora (lub innych prymitywów synchronizacji) wykonujących jakiś kod i zarządzania pewnymi strukturami potrzebnymi do śledzenia alokacji. | Sterta to obszar dynamicznie przydzielanej pamięci, który jest zarządzany automatycznie przez system operacyjny lub bibliotekę menedżera pamięci. Możesz przydzielić blok w dowolnym momencie i zwolnić go w dowolnym momencie. Alokacja sterty wymaga utrzymywania pełnego zapisu tego, jaka pamięć jest przydzielona, a co nie, a także wykonywania pewnych czynności konserwacyjnych w celu zmniejszenia fragmentacji, znalezienia ciągłych segmentów pamięci wystarczająco dużych, aby pasowały do żądanego rozmiaru i tak dalej. Pamięć można cofnąć w dowolnym momencie, pozostawiając wolne miejsce. W miarę wzrostu stosu nowe bloki są często przydzielane z niższych adresów do wyższych adresów. W ten sposób można myśleć o stercie jako o stercie bloków pamięci, które powiększają się w miarę przydzielania pamięci. Jeśli sterta jest zbyt mała dla alokacji, rozmiar można często zwiększyć, uzyskując więcej pamięci z bazowego systemu operacyjnego. Pamięć przydzielona ze sterty pozostanie przydzielona do momentu wystąpienia jednej z następujących sytuacji: Pamięć zostaje uwolniona Program kończy się Stos: Przechowywane w pamięci RAM komputera, tak jak sterta. Zmienne utworzone na stosie wyjdą poza zakres i zostaną automatycznie cofnięte. Znacznie szybsza alokacja w porównaniu ze zmiennymi na stercie. Przechowuje dane lokalne, adresy zwrotne, używane do przekazywania parametrów. Może dojść do przepełnienia stosu, gdy użyto zbyt dużej ilości stosu (głównie z nieskończonej lub zbyt głębokiej rekurencji, bardzo duże alokacje). Używałbyś stosu, gdybyś dokładnie wiedział, ile danych potrzebujesz alokować przed kompilacją i nie jest zbyt duży. Zwykle maksymalny rozmiar jest już określony w programie zaczyna. Sterta: Przechowywane w pamięci RAM komputera, podobnie jak stos. W C ++ zmienne na stercie muszą być niszczone ręcznie i nigdy wykraczają poza zakres. Dane są uwalniane za pomocą funkcji delete, delete [] lub bezpłatnie. Wolniej alokować w porównaniu ze zmiennymi na stosie. Używany na żądanie do przydzielania bloku danych do wykorzystania przez program. Może wystąpić fragmentacja, gdy jest dużo przydziałów i cofnięcia przydziałów. W C ++ lub C dane utworzone na stercie będą wskazywane przez wskaźniki i przydzielone odpowiednio za pomocą nowego lub malloc. Mogą wystąpić błędy alokacji, jeśli zażądano zbyt dużego bufora być przydzielone. tyużyje sterty, jeśli nie wiesz dokładnie, ile danych masz będą potrzebne w czasie wykonywania lub jeśli musisz przydzielić dużo danych. Odpowiedzialny za wycieki pamięci. | Stos jest zasadniczo łatwo dostępną pamięcią, która po prostu zarządza swoimi elementami jako - dobrze - stos. Na stos mogą trafiać tylko przedmioty, których rozmiar jest z góry znany. Tak jest w przypadku liczb, ciągów znaków, wartości logicznych. Sterta jest pamięcią na elementy, których nie można z góry określić dokładny rozmiar i struktura. Ponieważ obiekty i tablice można mutować i zmienią się w czasie wykonywania, muszą trafić na stertę. Źródło: Academind | Stos procesora i sterta są fizycznie związane z tym, jak procesor i rejestry współpracują z pamięcią, jak działa język asemblera maszynowego, a nie same języki wysokiego poziomu, nawet jeśli te języki mogą decydować o małych rzeczach. Wszystkie nowoczesne procesory działają zgodnie z „tą samą” teorią mikroprocesora: wszystkie są oparte na tak zwanych „rejestrach”, a niektóre są przeznaczone do „stosu”, aby uzyskać wydajność. Wszystkie procesory mają rejestry stosu od samego początku i jak wiem, zawsze tu były. Języki asemblera są takie same od początku, pomimo różnic ... aż do Microsoft i jego języka pośredniego (IL), który zmienił paradygmat posiadania języka asemblera maszyny wirtualnej OO. Więc w przyszłości będziemy mogli mieć trochę procesora CLI / CIL (jeden projekt MS). Procesory mają rejestry stosu, aby przyspieszyć dostęp do pamięci, ale są one ograniczone w porównaniu z wykorzystaniem innych rejestrów, aby uzyskać pełny dostęp do całej dostępnej pamięci dla procesu. Dlatego rozmawialiśmy o alokacjach stosów i stert. Podsumowując, generalnie sterta jest ogromna i powolna i jest przeznaczona dla „globalnych” instancji i zawartości obiektów, ponieważ stos jest mały i szybki oraz dla „lokalnych” zmiennych i referencji (ukrytych wskaźników, aby zapomnieć o zarządzaniu nimi). Więc kiedy używamy słowa kluczowego new w metodzie, referencja (int) jest tworzona na stosie, ale obiekt i cała jego zawartość (typy wartości, a także obiekty) jest tworzona w stercie, jeśli pamiętam. Ale lokalne elementarne typy wartości i tablice są tworzone na stosie. Różnica w dostępie do pamięci występuje na poziomie odwołań do komórek: adresowanie sterty, ogólnej pamięci procesu, wymaga większej złożoności w zakresie obsługi rejestrów procesora niż stos, który jest „bardziej” lokalnie pod względem adresowania, ponieważ stos procesora register jest używany jako adres bazowy, jeśli dobrze pamiętam. To dlatego, gdy mamy bardzo długie lub nieskończone powtarzające się wywołania lub pętle, szybko dochodzi do przepełnienia stosu, bez zamrażania systemu na nowoczesnych komputerach ... C # Heap (wczytywanie) kontra stos (w) w .NET Stack vs Heap: poznaj różnicę Alokacja pamięci klasy statycznej, w której jest przechowywana C # Co i gdzie jest stos i sterta? https://en.wikipedia.org/wiki/Memory_management https://en.wikipedia.org/wiki/Stack_register Zasoby języka asemblera: Samouczek programowania w asemblerze Podręczniki dla deweloperów oprogramowania architektur Intel® 64 i IA-32 | Dziękuję za naprawdę dobrą dyskusję, ale jako prawdziwy noob zastanawiam się, gdzie są przechowywane instrukcje? Na POCZĄTKU naukowcy decydowali się między dwiema architekturami (von NEUMANN, gdzie wszystko jest uważane za DANE i HARVARD, gdzie obszar pamięci był zarezerwowany na instrukcje, a inny na dane). Ostatecznie zdecydowaliśmy się na projekt von Neumanna i teraz wszystko jest uważane za „takie samo”. To utrudniało mi naukę montażu https://www.cs.virginia.edu/~evans/cs216/guides/x86.html ponieważ mówią o rejestrach i wskaźnikach stosu. Wszystko powyżej mówi o DANYCH. Domyślam się, że skoro instrukcja jest zdefiniowaną rzeczą z określonym śladem pamięci, trafiłaby na stos, a więc wszystkie „te” rejestry omówione w asemblerze są na stosie. Oczywiście wtedy pojawiło się programowanie obiektowe z instrukcjami i danymi wprowadzanymi do struktury, która była dynamiczna, więc teraz instrukcje również będą przechowywane na stosie? | Bardzo aktywne pytanie. Zdobądź 10 punktów reputacji, aby odpowiedzieć na to pytanie. Wymóg dotyczący reputacji pomaga chronić to pytanie przed spamem i brakiem odpowiedzi. Nie szukasz odpowiedzi? Przeglądaj inne pytania z tagami stosu zarządzania pamięcią, stosu agnostycznego, stosu dynamicznego przydzielania pamięci, lub zadaj własne pytanie.